home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / popserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-30  |  14.0 KB  |  688 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #ifdef UNIX
  12. #include <sys/types.h>
  13. #endif
  14. #include <sys/stat.h>
  15. #if    defined(__STDC__) || defined(__TURBOC__)
  16. #include <stdarg.h>
  17. #endif
  18. #include <ctype.h>
  19. #include <setjmp.h>
  20. #include "global.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop.h"
  27. #include "dirutil.h"
  28. #include "commands.h"
  29.  
  30. extern char Nospace[];
  31.  
  32. static struct pop_scb *create_scb __ARGS((void));
  33. static void delete_scb __ARGS((struct pop_scb *scb));
  34. static void popserv __ARGS((int s,void *unused,void *p));
  35. static int poplogin __ARGS((char *pass,char *username));
  36.  
  37. /* I don't know why this isn't static, it isn't called anywhere else {was} */
  38. void pop_sm __ARGS((struct pop_scb *scb));
  39.  
  40. static int Spop = -1; /* prototype socket for service */
  41.  
  42. /* Start up POP receiver service */
  43. int
  44. pop1(argc,argv,p)
  45. int argc;
  46. char *argv[];
  47. void *p;
  48. {
  49.     return (installserver (argc, argv, &Spop, "POP listener", IPPORT_POP,
  50.         "POP server", popserv, 2048, NULL));
  51. }
  52.  
  53. /* Shutdown POP service (existing connections are allowed to finish) */
  54.  
  55. int
  56. pop0(argc,argv,p)
  57. int argc;
  58. char *argv[];
  59. void *p;
  60. {
  61.     return (deleteserver (&Spop));
  62. }
  63.  
  64. static void
  65. popserv(s,unused,p)
  66. int s;
  67. void *unused;
  68. void *p;
  69. {
  70.     struct pop_scb *scb;
  71.  
  72.     sockowner(s,Curproc);        /* We own it now */
  73.     log(s,"open POP");
  74.  
  75.     if((scb = create_scb()) == NULLSCB) {
  76.         tprintf(Nospace);
  77.         log(s,"close POP - no space");
  78.         close_s(s);
  79.         return;
  80.     }
  81.  
  82.     scb->socket = s;
  83.     scb->state  = AUTH;
  84.  
  85.     (void) usprintf(s,greeting_msg,Hostname);
  86.  
  87. loop:    if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  88.         /* He closed on us */
  89.  
  90.         goto quit;
  91.     }
  92.  
  93.     rip(scb->buf);
  94.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  95.         goto loop;
  96.     pop_sm(scb);
  97.     if (scb->state == DONE)
  98.         goto quit;
  99.  
  100.     goto loop;
  101.  
  102. quit:
  103.     log(scb->socket,"close POP");
  104.     close_s(scb->socket);
  105.     delete_scb(scb);
  106. }
  107.  
  108.  
  109. /* Create control block, initialize */
  110.  
  111. static struct
  112. pop_scb *create_scb()
  113. {
  114.     register struct pop_scb *scb;
  115.  
  116.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  117.         return NULLSCB;
  118.  
  119.     scb->username[0] = '\0';
  120.     scb->msg_status = NULL;
  121.     scb->wf = NULL;
  122.  
  123.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  124.  
  125.     scb->folder_modified = FALSE;
  126.     return scb;
  127. }
  128.  
  129.  
  130. /* Free resources, delete control block */
  131.  
  132. static void
  133. delete_scb(scb)
  134. register struct pop_scb *scb;
  135. {
  136.  
  137.     if (scb == NULLSCB)
  138.         return;
  139.     if (scb->wf != NULL)
  140.         fclose(scb->wf);
  141.     if (scb->msg_status  != NULL)
  142.         free((char *)scb->msg_status);
  143.  
  144.     free((char *)scb);
  145. }
  146.  
  147. /* replace terminating end of line marker(s) (\r and \n) with null */
  148. void
  149. rrip(s)
  150. register char *s;
  151. {
  152.     register char *cp;
  153.  
  154.     if((cp = strchr(s,'\r')) != NULLCHAR)
  155.         *cp = '\0';
  156. #ifndef TNOS_68K
  157.     if((cp = strchr(s,'\n')) != NULLCHAR)
  158. #else
  159.     if((cp = strchr(s,'\l')) != NULLCHAR)
  160. #endif
  161.         *cp = '\0';
  162. }
  163.  
  164. /* --------------------- start of POP server code ------------------------ */
  165.  
  166. #define    BITS_PER_WORD        16
  167.  
  168. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  169.  
  170. /* Command string specifications */
  171.  
  172. static char    ackd_cmd[] = "ACKD",
  173.         acks_cmd[] = "ACKS",
  174. #ifdef POP_FOLDERS
  175.         fold_cmd[] = "FOLD ",
  176. #endif
  177.         login_cmd[] = "HELO ",
  178.         nack_cmd[] = "NACK",
  179.         quit_cmd[] = "QUIT",
  180.         read_cmd[] = "READ",
  181.         retr_cmd[] = "RETR";
  182.  
  183. void
  184. pop_sm(scb)
  185. struct pop_scb *scb;
  186. {
  187.     char password[40];
  188.     void state_error __ARGS((struct pop_scb *,char *));
  189.     void open_folder __ARGS((struct pop_scb *));
  190.     void do_cleanup __ARGS((struct pop_scb *));
  191.     void read_message __ARGS((struct pop_scb *));
  192.     void retrieve_message __ARGS((struct pop_scb *));
  193.     void deletemsg __ARGS((struct pop_scb *,int));
  194.     void get_message __ARGS((struct pop_scb *,int));
  195.     void print_message_length __ARGS((struct pop_scb *));
  196.     void close_folder __ARGS((struct pop_scb *));
  197. #ifdef POP_FOLDERS
  198.     void select_folder __ARGS((struct pop_scb *));
  199. #endif
  200.  
  201.     if (scb == NULLSCB)    /* be certain it is good -- wa6smn */
  202.         return;
  203.  
  204.     switch(scb->state) {
  205.     case AUTH:
  206.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  207.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  208.  
  209.             if (!poplogin(scb->username,password)) {
  210.                 log(scb->socket,"POP access DENIED to %s",
  211.                         scb->username);
  212.                 state_error(scb,"Access DENIED!!");
  213.                 return;
  214.             }
  215.  
  216.             log(scb->socket,"POP access granted to %s",
  217.                     scb->username);
  218.             open_folder(scb);
  219.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  220.             do_cleanup(scb);
  221.         } else
  222.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  223.         break;
  224.  
  225.     case MBOX:
  226.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  227.             read_message(scb);
  228.  
  229. #ifdef POP_FOLDERS
  230.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  231.             select_folder(scb);
  232.  
  233. #endif
  234.  
  235.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  236.             do_cleanup(scb);
  237.         } else
  238.             state_error(scb,
  239. #ifdef POP_FOLDERS
  240.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  241. #else
  242.                     "(MBOX) Expected READ or QUIT command");
  243. #endif
  244.         break;
  245.  
  246.     case ITEM:
  247.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  248.             read_message(scb);
  249.  
  250. #ifdef POP_FOLDERS
  251.  
  252.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  253.             select_folder(scb);
  254. #endif
  255.  
  256.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  257.             retrieve_message(scb);
  258.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  259.             do_cleanup(scb);
  260.         else
  261.             state_error(scb,
  262. #ifdef POP_FOLDERS
  263.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  264. #else
  265.                "(ITEM) Expected READ, RETR, or QUIT command");
  266. #endif
  267.         break;
  268.  
  269.     case NEXT:
  270.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  271.                 /* ACKD processing */
  272.             deletemsg(scb,scb->msg_num);
  273.             scb->msg_num++;
  274.             get_message(scb,scb->msg_num);
  275.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  276.                 /* ACKS processing */
  277.             scb->msg_num++;
  278.             get_message(scb,scb->msg_num);
  279.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  280.                 /* NACK processing */
  281.             fseek(scb->wf,scb->curpos,SEEK_SET);
  282.         } else {
  283.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  284.             return;
  285.         }
  286.  
  287.         print_message_length(scb);
  288.         scb->state  = ITEM;
  289.         break;
  290.  
  291.     case DONE:
  292.         do_cleanup(scb);
  293.         break;
  294.  
  295.     default:
  296.         state_error(scb,"(TOP) State Error!!");
  297.         break;
  298.     }
  299. }
  300.  
  301. void
  302. do_cleanup(scb)
  303. struct pop_scb *scb;
  304. {
  305.     void close_folder __ARGS((struct pop_scb *));
  306.  
  307.     close_folder(scb);
  308.     (void) usprintf(scb->socket,signoff_msg);
  309.     scb->state = DONE;
  310. }
  311.  
  312. void
  313. state_error(scb,msg)
  314. struct pop_scb *scb;
  315. char *msg;
  316. {
  317.     (void) usprintf(scb->socket,error_rsp,msg);
  318.     scb->state = DONE;
  319. }
  320.  
  321. #ifdef POP_FOLDERS
  322.  
  323. select_folder(scb)
  324. struct pop_scb    *scb;
  325. {
  326.     sscanf(scb->buf,"FOLD %s",scb->username);
  327.  
  328.     if (scb->wf != NULL)
  329.         close_folder(scb);
  330.  
  331.     open_folder(scb);
  332. }
  333.  
  334. #endif
  335.  
  336.  
  337. void
  338. close_folder(scb)
  339. struct pop_scb *scb;
  340. {
  341.     char folder_pathname[64];
  342.     char line[BUF_LEN];
  343.     FILE *fd;
  344.     int deleted = FALSE;
  345.     int msg_no = 0;
  346.     int newmail __ARGS((struct pop_scb *));
  347.     int isdeleted __ARGS((struct pop_scb *,int));
  348.  
  349.     if (scb->wf == NULL)
  350.         return;
  351.  
  352.     if (!scb->folder_modified) {
  353.         /* no need to re-write the folder if we have not modified it */
  354.  
  355.         fclose(scb->wf);
  356.         scb->wf = NULL;
  357.  
  358.         free((char *)scb->msg_status);
  359.         scb->msg_status = NULL;
  360.         return;
  361.     }
  362.  
  363.  
  364.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  365.  
  366.     if (newmail(scb)) {
  367.         /* copy new mail into the work file and save the
  368.            message count for later */
  369.  
  370.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  371.             state_error(scb,"Unable to add new mail to folder");
  372.             return;
  373.         }
  374.  
  375.         fseek(scb->wf,0,SEEK_END);
  376.         fseek(fd,scb->folder_file_size,SEEK_SET);
  377.         while (!feof(fd)) {
  378.             fgets(line,BUF_LEN,fd);
  379.             fputs(line,scb->wf);
  380.         }
  381.  
  382.         fclose(fd);
  383.     }
  384.  
  385.     /* now create the updated mail folder */
  386.  
  387.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  388.         state_error(scb,"Unable to update mail folder");
  389.         return;
  390.     }
  391.  
  392.     rewind(scb->wf);
  393.     while (!feof(scb->wf)){
  394.         fgets(line,BUF_LEN,scb->wf);
  395.  
  396.         if (isSOM(line)){
  397.             msg_no++;
  398.             if (msg_no <= scb->folder_len)
  399.                 deleted = isdeleted(scb,msg_no);
  400.             else
  401.                 deleted = FALSE;
  402.         }
  403.  
  404.         if (deleted)
  405.             continue;
  406.  
  407.         fputs(line,fd);
  408.     }
  409.  
  410.     fclose(fd);
  411.  
  412.     /* trash the updated mail folder if it is empty */
  413.     if(fsize(folder_pathname) == 0L)
  414.         unlink(folder_pathname);
  415.  
  416.     fclose(scb->wf);
  417.     scb->wf = NULL;
  418.  
  419.     free((char *)scb->msg_status);
  420.     scb->msg_status = NULL;
  421. }
  422.  
  423. void
  424. open_folder(scb)
  425. struct pop_scb    *scb;
  426. {
  427.     char folder_pathname[64];
  428.     char line[BUF_LEN];
  429.     FILE *fd;
  430.     FILE *tmpfile();
  431.  
  432.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  433.     scb->folder_len       = 0;
  434.     if((scb->folder_file_size=fsize(folder_pathname)) <= 0L) {
  435.          (void) usprintf(scb->socket,no_mail_rsp);
  436.          return;
  437.     }
  438.  
  439.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  440.         state_error(scb,"Unable to open mail folder");
  441.         return;
  442.     }
  443.  
  444.     if ((scb->wf = tmpfile()) == NULL) {
  445.         state_error(scb,"Unable to create work folder");
  446.         return;
  447.     }
  448.  
  449.     while(!feof(fd)) {
  450.         fgets(line,BUF_LEN,fd);
  451.  
  452.         /* scan for begining of a message */
  453.  
  454.         if (isSOM(line))
  455.             scb->folder_len++;
  456.  
  457.         /* now put  the line in the work file */
  458.  
  459.         fputs(line,scb->wf);
  460.     }
  461.  
  462.     fclose(fd);
  463.  
  464.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  465.  
  466.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  467.         (scb->msg_status_size == 0))
  468.         scb->msg_status_size++;
  469.  
  470.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  471.                 sizeof(unsigned int))) == NULL) {
  472.         state_error(scb,"Unable to create message status array");
  473.         return;
  474.     }
  475.  
  476.     (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  477.  
  478.     scb->state  = MBOX;
  479. }
  480.  
  481. void
  482. read_message(scb)
  483. struct pop_scb    *scb;
  484. {
  485.     void get_message __ARGS((struct pop_scb *,int));
  486.     void print_message_length __ARGS((struct pop_scb *));
  487.  
  488.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  489.         return;
  490.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  491.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  492.     else
  493.         scb->msg_num++;
  494.  
  495.     get_message(scb,scb->msg_num);
  496.     print_message_length(scb);
  497.     scb->state  = ITEM;
  498. }
  499.  
  500. void
  501. retrieve_message(scb)
  502. struct pop_scb    *scb;
  503. {
  504.     char line[BUF_LEN];
  505.     long cnt;
  506.     void rrip __ARGS((char *));
  507.  
  508.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  509.         return;
  510.     if (scb->msg_len == 0) {
  511.         state_error(scb,"Attempt to access a DELETED message!");
  512.         return;
  513.     }
  514.  
  515.     cnt  = scb->msg_len;
  516.     while(!feof(scb->wf) && (cnt > 0)) {
  517.         fgets(line,BUF_LEN,scb->wf);
  518.         rrip(line);
  519.  
  520.         (void) usprintf(scb->socket,msg_line,line);
  521.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  522.     }
  523.  
  524.     scb->state = NEXT;
  525. }
  526.  
  527. void
  528. get_message(scb,msg_no)
  529. struct pop_scb    *scb;
  530. int msg_no;
  531. {
  532.     char line[BUF_LEN];
  533.     long ftell();
  534.     void rrip __ARGS((char *));
  535.  
  536.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  537.         return;
  538.     scb->msg_len = 0;
  539.     if (msg_no > scb->folder_len) {
  540.         scb->curpos  = 0;
  541.         scb->nextpos = 0;
  542.         return;
  543.     } else {
  544.         /* find the message and its length */
  545.  
  546.         rewind(scb->wf);
  547.         while (!feof(scb->wf) && (msg_no > -1)) {
  548.             if (msg_no > 0)
  549.                 scb->curpos = ftell(scb->wf);
  550.             
  551.             fgets(line,BUF_LEN,scb->wf);
  552.             rrip(line);
  553.  
  554.             if (isSOM(line))
  555.                 msg_no--;
  556.  
  557.             if (msg_no != 0)
  558.                 continue;
  559.  
  560.             scb->nextpos  = ftell(scb->wf);
  561.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  562.         }
  563.     }
  564.  
  565.     if (scb->msg_len > 0)
  566.         fseek(scb->wf,scb->curpos,SEEK_SET);
  567.  
  568.     /* we need the pointers even if the message was deleted */
  569.  
  570.     if  (isdeleted(scb,scb->msg_num))
  571.         scb->msg_len = 0;
  572. }
  573.  
  574. static int
  575. poplogin(username,pass)
  576. char *pass;
  577. char *username;
  578. {
  579.     char buf[80];
  580.     char *cp;
  581.     char *cp1;
  582.     FILE *fp;
  583.  
  584.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  585.         /* User file doesn't exist */
  586.         tprintf("POP users file %s not found\n",Popusers);
  587.         return(FALSE);
  588.     }
  589.  
  590.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  591.         if(buf[0] == '#')
  592.             continue;    /* Comment */
  593.  
  594.         if((cp = strchr(buf,':')) == NULLCHAR)
  595.             /* Bogus entry */
  596.             continue;
  597.  
  598.         *cp++ = '\0';        /* Now points to password */
  599.         if(strcmp(username,buf) == 0)
  600.             break;        /* Found user name */
  601.     }
  602.  
  603.     if(feof(fp)) {
  604.         /* User name not found in file */
  605.  
  606.         fclose(fp);
  607.         return(FALSE);
  608.     }
  609.     fclose(fp);
  610.  
  611.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  612.         return(FALSE);
  613.  
  614.     *cp1 = '\0';
  615.     if(strcmp(cp,pass) != 0) {
  616.         /* Password required, but wrong one given */
  617.  
  618.         return(FALSE);
  619.     }
  620.  
  621.     /* whew! finally made it!! */
  622.  
  623.     return(TRUE);
  624. }
  625.  
  626. int
  627. isdeleted(scb,msg_no)
  628. struct pop_scb *scb;
  629. int msg_no;
  630. {
  631.     unsigned int mask = 1,offset;
  632.  
  633.     msg_no--;
  634.     offset = msg_no / BITS_PER_WORD;
  635.     mask <<= msg_no % BITS_PER_WORD;
  636.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  637. }
  638.  
  639. void
  640. deletemsg(scb,msg_no)
  641. struct pop_scb *scb;
  642. int msg_no;
  643. {
  644.     unsigned int mask = 1,offset;
  645.  
  646.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  647.         return;
  648.     msg_no--;
  649.     offset = msg_no / BITS_PER_WORD;
  650.     mask <<= msg_no % BITS_PER_WORD;
  651.     scb->msg_status[offset] |= mask;
  652.     scb->folder_modified = TRUE;
  653. }
  654.  
  655. int
  656. newmail(scb)
  657. struct pop_scb *scb;
  658. {
  659.     char folder_pathname[64];
  660.     long newsize;
  661.  
  662.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  663.  
  664.     if((newsize=fsize(folder_pathname)) < 0L) {
  665.         state_error(scb,"Unable to get old mail folder's status");
  666.         return(FALSE);
  667.     } else
  668.         return ((newsize > scb->folder_file_size)? TRUE:FALSE);
  669. }
  670.  
  671. void
  672. print_message_length(scb)
  673. struct pop_scb *scb;
  674. {
  675.     char *print_control_string;
  676.  
  677.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  678.         return;
  679.     if (scb->msg_len > 0)
  680.         print_control_string = length_rsp;
  681.     else if (scb->msg_num <= scb->folder_len)
  682.         print_control_string = length_rsp;
  683.     else
  684.         print_control_string = no_more_rsp;
  685.  
  686.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  687. }
  688.